package com.zan.seckillweb.controller;

import org.apache.rocketmq.client.producer.SendCallback;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @Author Zan
 * @Create 2024/9/2 13:52
 * @ClassName: SeckillController
 * @Description : TODO 请用一句话描述该类的功能
 */
@RestController
public class SeckillController {

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Resource
    private RocketMQTemplate rocketMQTemplate;

    // CAS Java层面无锁，操作系统有锁
    AtomicInteger userIdAtomic = new AtomicInteger(0);

    /**
     * 1.redis去重
     * 2.库存的预扣减
     * 3.消息放入MQ
     * @param goodsId
     * @param userId 真实的项目中，会做登录，不会主动传userId，SecurityContextHolder.getContext().getAuthentication().getPrincple(); 之前拿到userId可以用这个拿
     * @return
     */
    @GetMapping("seckill")
    public String doSecKill(Integer goodsId/*, Integer userId*/) {
        int userId = userIdAtomic.incrementAndGet();
        // uk uniqueKey = [yyyyMMdd可选] + userId + goodsId 年月日是为了，当该天抢完后，第二天还是能继续抢
        String uk = userId + "-" + goodsId;
        // setIfAbsent = setnx
        Boolean flag = redisTemplate.opsForValue().setIfAbsent(uk, "");
        if (!flag) {
            return "您已经参与过该商品的抢购，请参与其他商品(❁´◡`❁)";
        }
        // 预扣减库存，假设redis存入 goodsId : stock
        // 记住：先查再改再更新是线程不安全的，不安全的操作
        // 因此直接减
        Long count = redisTemplate.opsForValue().decrement("goodsId:" + goodsId);
        if (count < 0) {
            return "该商品已经被抢完了，下次早点来(●'◡'●)";
        }
        // 放MQ异步处理
        rocketMQTemplate.asyncSend("seckillTopic4", uk, new SendCallback() {
            @Override
            public void onSuccess(SendResult sendResult) {
                System.out.println("消息发送成功");
            }

            @Override
            public void onException(Throwable throwable) {
                System.out.println("消息发送失败:" + throwable.getMessage());
                System.out.println("用户的id:" + userId + " 商品Id:" + goodsId);
            }
        });
        return "正在拼命抢购中，请稍后去订单中心查看";
    }
}
